package org.smile.db;

import java.io.IOException;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.Map;
import java.util.ResourceBundle;

import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NameNotFoundException;
import javax.naming.NamingException;
import javax.sql.DataSource;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

import org.apache.log4j.Logger;
import org.smile.db.pool.DataSourceContext;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;
/**
 * 数据库连接工具管理
 * 
 * @author strive
 *
 */
public class DbManager
{
	protected final static Logger logger = Logger.getLogger(DbManager.class.getName());
	
	private static Context context=null;
	/**
	 * jdbc配置信息
	 */
	private static Map jdbcMap=new HashMap();
	/**
	 * 默认连接数据源名称
	 */
	private static String default_datasource;
	/**
	 * 连接池数据源信息
	 */
	private static Map<String, DataSource> dsMap=new HashMap();
	
	
	static{
			init();
	}
	/**
	 * 清除所有的数据库连接信息
	 * @throws NameNotFoundException
	 */
	public static void clear() throws NameNotFoundException{
		jdbcMap.clear();
		default_datasource=null;
		DataSourceContext.unbindAll();
		dsMap.clear();
	}
	/**
	 * 重新计取配置文件，重置管理器
	 */
	public static void reset(){
		try {
			clear();
			DataSourceContext.rebindAll();
		} catch (Exception e) {
			logger.error("重置DbManager出错:",e);
		}
		init();
	}
	/**
	 * 初始化 从system_config.xml中
	 */
	private static void init(){
		 try {
			context=new InitialContext();
		} catch (NamingException e) {
			logger.error(e);
		}
		InputStream is= DbManager.class.getClassLoader().getResourceAsStream("system_config.xml");
		if(is==null){
			logger.info("system_config.xml 文件不存在,没有使用simle对数据源进行管理………………………………………………");
			return;
		}
		Document doc=null;
		try {
			doc = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(is);
		} catch (SAXException e) {
			logger.error("读取配置文件[system_config.xml]出错:",e);
		} catch (IOException e) {
			logger.error("读取配置文件[system_config.xml]出错:",e);
		} catch (ParserConfigurationException e) {
			logger.error("读取配置文件[system_config.xml]出错:",e);
		}
		//先加载jdbc配置信息
		NodeList nodeList=doc.getElementsByTagName("jdbc");
		Element element;
		int i=0,size=nodeList.getLength();
		for(i=0;i<size;i++)
		{
			element=(Element)nodeList.item(i);
			String name=element.getAttribute("name");
			String url=element.getAttribute("url");
			String driver=element.getAttribute("driver");
			String username=element.getAttribute("username");
			String password=element.getAttribute("password");
			Map map=new HashMap();
			map.put("url",url);
			map.put("driver", driver);
			map.put("username", username);
			map.put("password",password);
			jdbcMap.put(name, map);
			logger.info("加载jdbc配置"+name+"[url:"+url+",driver:"+driver+"]成功");
		}
		//加载数据源信息
		NodeList dataSourceList=doc.getElementsByTagName("DataSource");
		size=dataSourceList.getLength();
		for(i=0;i<size;i++)
		{
			element=(Element)dataSourceList.item(i);
			String name=element.getAttribute("name");
			String ref=element.getAttribute("ref");
			String isDefault=element.getAttribute("default");
			if("yes".equals(isDefault)){
				default_datasource=name;
			}
			//加入到DbManager中
			try {
				dsMap.put(name,getDataSource(ref));
				logger.info("加载数据源配置[name:"+name+",ref:"+ref+"]成功");
			} catch (NamingException e) {
				logger.error("初始化数据源出错",e);
			}
			
		}
		
	}
	/**
	 * 获取已经被DbManager管理的所有的数据源
	 * @return
	 */
	public static Map getManagedDataSources(){
		return dsMap;
	}
	/**
	 * 获取已经被DbManager管理的所有的数据源
	 * @param name system_config中配置的名称
	 * @return
	 */
	public static DataSource getManagedDataSource(String name){
		return dsMap.get(name);
	}
	/**
	 * 获得数据库连接
	 * @return
	 * @throws Exception 
	 * @throws SQLException
	 * @throws IOException
	 */
	public static Connection getJDBCConnection(String name)
	{
		try{
			Map map=(Map)jdbcMap.get(name);
			if(map==null){
				logger.error("没有配置一个名称为["+name+"]的JDBC连接信息");
			}else{
				return getJDBCConnection((String)map.get("driver"),(String)map.get("url"),(String)map.get("username"),(String)map.get("password"));
			}
		}catch(Exception e){
			logger.error("JDBC连接数据库出错:",e);
		}
		return null;
	}
	/**
	 * 获取数据库连接
	 * @param driverName
	 * @param url
	 * @param userName
	 * @param password
	 * @return
	 */
	public static Connection getJDBCConnection(String driverName,String url,String userName,String password){
		try
		{
			Class.forName(driverName).newInstance();
			Connection conn=DriverManager.getConnection(url, userName, password);
			logger.info("数据库连接成功"+conn);
			return conn;
		} catch (Exception e)
		{
			logger.error("连接数据库[url："+url+"]出现错误",e);
		}
		return null;
	}
	/**
	 * 从一个配置文件读取信息 获得一个连接
	 * @param configFile
	 * @return
	 */
	public static Connection getJDBCConnFormBundle(String configFile)
	{
		try{
			ResourceBundle dbResources = ResourceBundle.getBundle(configFile);
			String driver=dbResources.getString("driver");
			String url=dbResources.getString("url");
			String user=dbResources.getString("user");
			String password=dbResources.getString("password");
			Class.forName(driver).newInstance();
			return DriverManager.getConnection(url, user, password);
		}catch(Exception e){
			logger.error("从一个properties["+configFile+"]文件创建一个数据库连接出错:请确认此文件是否正确配置",e);
		}
		return null;
	}
	/**
	 * 得到smile 数据源
	 * @param _jndi 数据源名称
	 * @return 数据源
	 */
	public static DataSource getSmileDataSource(String _jndi){
		//从smile实现的数据源中配置文件中初始化
		try{
			return DataSourceContext.lookup(_jndi);
		}catch(NameNotFoundException e){
			logger.error("no datasource find from smile datasource context named "+_jndi+" …………");
		}
		return null;
	}
	/**
	 * 以数据源的名称查询数据源
	 * 此名称不是DbManager中管理的名称 
	 *   而是 真实的数据源容器中的名称 如：tomcat中配置的名称 
	 *    他的查询顺序为 ：
	 *    			1、从 system_config.xml 中 查询<resource />标签配置的数据源,即org.huzs.db.pool.BasicDataSource实现的数据源
	 *              2、如果没有则会 以["java:comp/env/"+_jndi]这样的规则去InitialContext中查找
	 *              3、如果还是没有 则会以_jndi为名称去InitialContext中查找，因为有的服务器中的连接池规则是不需要java:comp/env/做为前缀的
	 *    此方法不推荐手动调用 建设在system_config中配置一个数据源参数 
	 *   
	 * @return
	 * @throws NamingException 
	 */
	public  static DataSource getDataSource(String _jndi) throws NamingException
	{
		DataSource ds=dsMap.get(_jndi);
		if(ds!=null){
			return ds;
		}else{
			//从smile实现的数据源中配置文件中初始化
			try{
				ds= DataSourceContext.lookup(_jndi);
			}catch(NameNotFoundException e){
				//服务器中查找
				try{
					ds=(DataSource) context.lookup("java:comp/env/"+_jndi); 
				}catch(NameNotFoundException ee){
					try{
						ds=(DataSource) context.lookup(_jndi);
					}catch(NameNotFoundException eee){
						ds=(DataSource) context.lookup("java:comp/env/jdbc/"+_jndi);
					}
				}
			}
			dsMap.put(_jndi, ds);
		}
		return ds;
	}
	/**
	 * 得到默认的数据库连接
	 * @return
	 */
	public static Connection getConnection()
	{
		try {
			if(default_datasource==null){
				logger.error("你没有配置默认的数据源连接,请在system_config.xml文件中指定");
			}else{
				return dsMap.get(default_datasource).getConnection();
			}
		} catch (Exception e) {
			logger.error("连接数据库失败,从被管理的默认数据源["+default_datasource+"]",e);
		}
		return null;
	}
	/**
	 * 指这一个数据源名称得到数据库连接
	 * @param jndi
	 * @return
	 */
	public static Connection getConnection(String name)
	{
		Connection conn=null;
		try {
			DataSource ds=dsMap.get(name);
			if(ds!=null){
				conn=ds.getConnection();
			}else{
				conn=getDataSource(name).getConnection();
			}
			
		} catch (Exception e) {
			logger.error("连接数据库失败,从被管理的数据源["+name+"]",e);
		}
		return conn;
	}
}

